package com.yjzx.util.common.util.map;

import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * @author yjzx
 * @date 2023/6/12
 * @Description
 */
public class StopWatchMap<T> {
    /**
     * 存在时间
     */
    private final long SEC;
    /**
     * 开启null有默认值
     */
    private final T NULL_VALUE;
    /**
     * 是否开启基本数据类型同值同key,1==1L
     *
     * @param numericTypes
     */
    private final boolean ENABLE_NUMERIC_TYPE;
    private final boolean ENABLE_REFERENCE;

    private Map<Serializable, NodeMap<LocalDateTime, ?>> map;

    public StopWatchMap(long sec, boolean numericTypeEnable, boolean referenceEnable, T nullValue, Class<Map> clazz) {
        this.SEC = sec;
        this.ENABLE_NUMERIC_TYPE = numericTypeEnable;
        this.ENABLE_REFERENCE = referenceEnable;
        this.NULL_VALUE = nullValue;
        if (clazz == null) {
            this.map = new HashMap<>(64);
        } else {
            try {
                this.map = clazz.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
                this.map = new HashMap<>(64);
            }
        }
    }

    private static Serializable transKey(Serializable key) {
        Serializable k;
        if (key instanceof Number) {
            if (key instanceof Integer || key instanceof Long || key instanceof Byte || key instanceof Short) {
                k = ((Number) key).longValue();
            } else if (key instanceof Double || key instanceof Float) {
                k = ((Number) key).doubleValue();
            } else {
                k = key;
            }
        } else {
            k = key;
        }
        return k;
    }

    private Serializable getKey(Serializable key) {
        return key = ENABLE_NUMERIC_TYPE ? transKey(key) : key;
    }

    private T getValue(Object o) {
        if (ENABLE_REFERENCE) {
            Reference<T> reference = (Reference<T>) o;
            if (reference != null) {
                return reference.get();
            } else {
                return null;
            }
        } else {
            return (T) o;
        }
    }
    private Object setValue(T t){
        if (ENABLE_REFERENCE) {
            return new SoftReference<>(t);
        } else {
            return t;
        }
    }

    public T get(Serializable key) {
        key = getKey(key);
        if (key != null) {
            NodeMap<LocalDateTime, ?> NodeMap = map.get(key);
            if (NodeMap != null && NodeMap.getKey().plusSeconds(SEC).compareTo(LocalDateTime.now()) > 0) {
                return getValue(NodeMap.getValue());
            }
        }
        return null;
    }

    public void remove(Serializable key) {
        if (key != null) {
            map.remove(getKey(key));
        }
    }

    public void remove(Serializable... keys) {
        if (keys != null) {
            for (Serializable key : keys) {
                if (key != null) {
                    remove(key);
                }
            }
        }
    }

    public void removeAll() {
        map = new HashMap<>(64);
    }

    public T put(Serializable key, T value) {
        if (key != null && value != null) {
            map.put(transKey(key), new NodeMap<>(LocalDateTime.now(), setValue(value)));
            return value;
        } else {
            return null;
        }
    }
}
